/**
 * Created by haua on 16/1/28.
 */
(function(){
    if(!window.H)
        window.H = {};
    
    /**
     * 用路径换一个url，url是外网域名+80端口+路径
     * @param dir string 必须是/开头的路径，或者带域名的
     * */
    var domain = 'http://wdzj1314.com';
    H.changeUrl = function(dir){
        if(dir.indexOf('http')==0)
            return dir;
        var i = dir.indexOf('://');
        if(i>0){
            var ii = dir.indexOf('/',i+3);
            dir = dir.slice(ii);
        }
        return domain + this.trim(dir);
    };

    /**
     * 封装的一个跳转函数，可以做到返回时直接跳过当前页到上一个页面
     * @param url string
     * @param [clearThis]=false boolean 是否清除本页记录，让下一页直接返回本页的上一页
     * */
    H.jump = function(url,clearThis){
        if(clearThis)
            location.replace(url);
        else
            location.href = url;
    };

    /**
     * 获取一个url中的目录
     * @param dir string
     * */
    H.getDir = function (dir){
        var dirArr = dir.split('/');
        if(!dirArr[dirArr.length-1])

            if(dirArr[dirArr.length-1].indexOf('.')>=0)
                dirArr.pop();
        return dirArr.join('/')+'/';
    };

    /**
     * 四舍五入到小数多少位
     * @param int int 原数值
     * @param i number 精确到多少位，如果为负数，则会返回科学计数法的string
     * */
    H.round = function(int,i){
        if(i>0){
            var d = i*10;
            return Math.round(int*d)/d;
        }else
            return Math.round(int);
    };
    /**
     * 计算
     * */
    H.plus = function(a){
        var e = 0;
        if($.isArray(a))
            for(var i=0;i< a.length;i++)
                e+=parseFloat(a[i]);
        return e;
    };

    /**
     * 替换url中的queryString的值
     * @param url
     * @param k object|string 这个可以是键值对的对象，也可以只是key名
     * @param v
     * */
    H.urlReplaceArg = function(url,k,v){
        if(typeof url!='string')return url;
        if(typeof k!='object'){
            var kk = {};
            kk[k] = v;
            k = kk;
        }
        for(var i in k){
            if(k.hasOwnProperty(i)){
                var reg = new RegExp('([&?]'+i+'\\=)[^?&]*','g');//两个右斜杠，在变为reg后会变成一个的
                if(url.search(reg)>=0){
                    // 这是只匹配(?&)和其后面的值，匹配出来的$1是?&之一。
                    // 原字符串未被正则匹配的部分是不会变的，replace之后只改匹配成功的部分，而不是只生成后面表达式的部分
                    url = url.replace&&url.replace(reg,'$1'+k[i]);
                }else{
                    url = url+(url.indexOf('?')>=0?'&':'?')+i+'='+k[i];
                }
            }
        }
        return url;
    };
    /**
     * 除了清除字符串前后的空格，还能清除DOM对象的空格元素
     * */
    H.trim = function(str){
        if(typeof str=='string')
            return str.replace(/(^\s+)|(\s+$)/g,'');
        else if(str instanceof HTMLElement&&str.childNodes){
            var childrens = str.childNodes,i=0;
            for(;i<childrens.length;){
                if(childrens[i].nodeType==3){
                    str.removeChild(childrens[i]);
                }else
                    i++;
            }
        }else{
            return str;
        }
    };
    H.trimAll = function(str){
        return str.replace(/\s/g,'');
    };

    H.isTouchDevice = function(){
        return ('ontouchstart' in document.documentElement);
    };

    /**
     * 打包好的，兼容的，获取各种信息的方法
     * @param name String 要获取的是什么信息
     */
    H.inf = function(name){
        var obj;
        switch(name){
            case 'windowHeight':
                obj = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight;
                break;
            case 'windowWidth':
                obj = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth;
                break;
        }
        return obj
    };

    /**
     * 原生ajax请求
     * @param url String 响应请求的链接
     * @param options object/function
     * @param options.args Object 传给服务器的参数，也可以使用String，但是内容要用键值对的方式写好，参数之间用&
     * @param options.method String 值："POST"或"GET"。默认：如果有args值就是POST，没有就GET
     * @param options.complete Function 成功或者失败都回调的函数
     * @param options.cache Boolean 是否缓存
     * @param options.dataFormat String 值："JSON": 返回 JSON 数据,"String": 返回 String 数据(ps.现在返回的数据都是原始数据，不会给你转的！)
     *
     * ps.输出的数据可能需要手动转换成json：var data = JSON.parse(data);
     */
    H.ajax = function(url,options){
        var req = new XMLHttpRequest(),
            complete = options.complete||options,
            method = options.method||"POST",
            dataFormat = options.dataFormat||'JSON',
            cache = options.cache||true,
            args = options.args||'';

        url = url+(cache?'':((url.indexOf('?')>=0?'&':'?')+"random="+Math.random()));

        if(complete){
            req.onreadystatechange = function(){
                if (req.readyState==4 && req.status==200){
                    //晚一些用try...catch来判断数据格式
                    //var reData = req.responseText;
                    //if(dataFormat=='JSON')reData = JSON.parse(reData);//JSON.stringify()
                    complete(req.responseText);
                    //complete.apply(null,[req.responseText]);
                }
                //else console.log('[Hquery]请求未完成或者出错:',req);
            }
        }
        req.open(method,url,true);
        var argString;
        if(args){
            req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            argString = '';
            if(typeof args=='string'){
                argString = args;
            }else{
                for(key in args){
                    if(!(args[key]===undefined||args[key]===null))argString += key + '=' + encodeURIComponent(args[key]) + '&';
                }
            }
            argString = argString.replace(/(^&+)|(&+$)/g,'');//去掉前后的&符号
        }
        req.send(argString);
    };

    function ifSuppLocalStora(){
        if(!window.localStorage){
            console.error('[Hquery]浏览器不支持本地存储');
            return false;
        }else{
            return true;
        }
    }
    /**
     * 本地存储，说明 http://www.cnblogs.com/xiaowei0705/archive/2011/04/19/2021372.html
     * @param key String 到时候取数据要用来做参数的
     * @param value all 存储的值，支持所有数据格式（已测试的格式包括：String、Array、Object）
     * @param time int 缓存存活的时间，单位秒
     * 如果参数有两个，则前面是key后面是value，
     * 如果只有一个参数，会返回一个随机key
     */
    H.dataTimeName = '_Htime';
    /**
     * 把数据存储到localStorage
     * @param key
     * @param value
     * @param time int 单位秒，数据存活时间
     * @return boolean|int 2:存储内容超过5M。1:不支持localStorage
     * */
    H.saveData = function(key,value,time){
        if(!ifSuppLocalStora())return 1;
        if(key&&typeof value!='undefined'){
            var valueStr = JSON.stringify(value);
            if(this.strLength(valueStr)>=4500000){
                return 2;
            }
            try{
                this.delData(key);
                localStorage[key] = valueStr;
                //设置缓存时间的原理是，再在localStorage存储一个时间值，
                time = parseInt(time);
                if(time){
                    localStorage[key+this.dataTimeName] = (new Date().getTime())+time*1000;
                }
                return true;
            }catch(e){
                return 2;
            }
        }
        return 0;
    };
    H.getData = function(key,del){
        if(!ifSuppLocalStora())return;

        var timeKey = key+this.dataTimeName,
            time=localStorage[timeKey];
        if(time&&time<(new Date().getTime())){
            localStorage.removeItem(key);
            localStorage.removeItem(timeKey);
            return false;
        }

        var value = H.json(localStorage[key]);
        if(del)this.delData(key);
        return value;
    };
    H.delData = function(key){
        if(!ifSuppLocalStora())return;
        localStorage.removeItem(key);
    };


    /**
     * 加载js，css等文件
     * @param url String 文件的url
     * @param type String 指定文件类型(省略后，此参数的位置是fn)
     * @param fn Function/object 加载完成后执行的方法
     * @param fn.type String 指定文件类型
     * @param fn.complete Function 加载完成后执行的方法
     */
    H.loadFile = function(url,type,fn){
        fn = typeof type=='string'?{complete:fn,type:type}:type;

        var filetype = fn.type || url.split('.').pop(),
            fileref;
        fn = fn.complete || fn;

        if(filetype == "js"){
            fileref = document.createElement('script');
            fileref.setAttribute("type","text/javascript");
            fileref.setAttribute("src",url);
        }else if(filetype == "css"){
            fileref = document.createElement('link');
            fileref.setAttribute("rel","stylesheet");
            fileref.setAttribute("type","text/css");
            fileref.setAttribute("href",url);
        }else{
            console.error('[Hquery]H.loadFile:未知的文件类型');
        }

        if(fn)fileref.onload = fn;
        //console.log(fileref);
        if(typeof fileref != "undefined"){
            document.getElementsByTagName("head")[0].appendChild(fileref);
        }
    };

    H.queryString = function(url){
        var queryString = (url||location.href).split('?');
        if(!queryString[1])
            return {};
        else
            queryString = queryString[1].split('#');
        var search = queryString[0].split('&'),
            sNum=search.length,
            obj={'_hash':queryString[1]};
        for(var i=0;i<sNum;i++){
            var arr = search[i].split('=');
            obj[arr[0]] = decodeURIComponent(arr[1]);
        }
        return obj
    };

    /**
     * 合并对象的属性（不包括原型链中的）
     * */
    H.extend = function(reWrite){
        var args = this.makeArray(arguments),i = 0,reObj = args.shift(),key,arg,W = false;
        if(typeof reWrite=='boolean'){
            W = reWrite;
            reObj = args.shift();
            if(!reObj)return false;
        }
        var aNum = args.length;
        for(;i<aNum;i++){
            arg = args[i];
            if(arg&&typeof arg=='object')
                for(key in arg){
                    if(!W&&reObj[key])
                        continue;
                    if(arg.hasOwnProperty(key))
                        reObj[key] = args[i][key];
                }
        }
        return reObj;
    };

    /**
     * 把字符串转为json或者json转为字符串
     * @param data allkind 支持所有数据格式，但是如果是非json格式的字符串，或者转换失败会返回本身字符串
     * @param [tranTo] String 强制转换为什么格式，如果本身就是那种格式，会返回原数据
     *						(主要用于不确定原数据是什么格式，但是又有可能需要转格式的时候)
     *						值：string ：原始字符串数据
     *							json ：js对象
     */
    H.json = function(data,tranTo){
        if(!data)return '';

        if(typeof data=='string'){//origin -> json
            //data = data.replace(/\s*/g,'');//把所有空格都去掉
            data = $.trim(data);
            var firstLett = data.slice(0,2);
            if(tranTo=='string'||firstLett.search(/\[|\{|\"|\'/i)<0){//如果这个原本是字符串的，直接返回就行了，url编码会自动解码的
                return data
            }
            try{
                var i=0;
                while(typeof data=='string'&&i<5){//不知道为啥，存进localStage里的数据取出来的时候要JSON.parse两次...
                    data = JSON.parse(data);
                    ++i;
                }
            }
            catch(e){console.error('[Hquery error]'+e)}

            return data
        }else{//json -> origin
            if(tranTo=='json'){
                return data
            }
            return JSON.stringify(data)
        }
    };

    H.error = function(){
        if(navigator.userAgent.search(/MicroMessenger|ZujiBrowser/i)>=0){
            var outPutStr = '';
            for(var i= 0,aNum=arguments.length;i<aNum;i++){
                outPutStr += '\n'+H.json(arguments[i],'origin');
            }
            alert(outPutStr);
        }else{
            console.error.apply(console,arguments);
        }
    };

    /**
     * 生成随机字符串
     * @author hhh
     * @param length num 生成的数是多少位的
     * @return String 生成的随机字符串
     */
    H.getRandChar = function(length){
        var str = null,
            strDict = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz-_~",
            max = strDict.length;
        length = length||8;

        for(var i=0;i<length;i++){
            str += strDict.charAt(Math.floor(Math.random()*max));
        }

        return str;
    };

    /**
     * 1    Element
     * 2	Attribute
     * 3	Text
     * 4	CDATA Section
     * 5	Entity Reference
     * 6	Entity
     * 7	Processing Instrucion
     * 8	Comment
     * 9	Document
     * 10	Document Type
     * 11	Document Fragment
     * 12	Notation
     * */
    H.isNode = function(x){
        return !!(x && x.nodeType);
    };
    H.isElement = function(x){
        return x && (x.nodeType?x.nodeType==1:false);
    };

    /**
     * 判断对象是否为数组
     */
    H.isArray = function(x){
        return x instanceof Array;
    };

    /**
     * 把类数组的对象转为数组
     * @param arrayObj object 类数组的对象
     * @param [j=arrayObj.length] 新数组元素数，如果原对象没有length属性，则必须设置
     * @param [i=0] int 新数组从原对象中第几个元素开始。
     * */
    H.makeArray = function(arrayObj, j, i){
        return Array.prototype.slice.call(arrayObj, i || 0, j || arrayObj.length);
    };

    /**
     * 判断对象是否为object
     */
    H.isObject = function(x){
        return (typeof x === "function" || (typeof x === "object" && x !== null));
    };

    /**
     * 获取元素的经过计算后的所有样式
     * */
    H.getComputedStyle = function(elem,classes){
        return (window.getComputedStyle?window.getComputedStyle(elem||document.documentElement,classes||null):elem.currentStyle) || null;
    };
    /**
     * 获取浏览器各种前缀
     * */
    H.prefix = function(){
        var styles = this.getComputedStyle(document.documentElement),
            pre = (Array.prototype.slice
                    .call(styles)
                    .join('')
                    .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
            )[1],
            dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1];
        return {
            dom: dom,
            lowercase: pre,
            css: '-' + pre + '-',
            js: pre[0].toUpperCase() + pre.substr(1)
        };
    };

    var animEndEventNames = {
            'webkit' : 'webkitAnimationEnd',
            'o' : 'oAnimationEnd',
            'ms' : 'MSAnimationEnd',
            'moz':'mozTransitionEnd',
            'normal' : 'animationend'
        },
        transitionendName = {
            'webkit' : 'webkitTransitionEnd',
            'o' : 'oTransitionEnd',
            'ms' : 'MSTransitionEnd',
            'normal' : 'transitionend'
        };
    //当css3动画结束时会发生的事件，这里返回的只是方法名的字符串，要把字符串用on()才行
    H.animationend = function(){
        return animEndEventNames[H.prefix().lowercase||'normal'];
    };
    //使用Transition动画结束后的事件
    H.transitionEnd = function(){
        return transitionendName[H.prefix().lowercase||'normal'];
    };


    H.inArray = function(item,array){
        var aNum = array.length;
        for(var i=0;i<aNum&&array[i]!=item;i++);
        return i<aNum;
    };
    /**
     * 删除数组中指定值，并返回新数组
     * */
    H.arrayRemove = function(value,array){
        if(!$.isArray(array))
            H.error('[H.arrayRemove]','这不是数组哦');
        var newArr = [];
        for(var i= 0,aNum=array.length;i<aNum;i++){
            if(array[i]!==value)
                newArr.push(array[i]);
        }
        return newArr;
    };

    /**
     * 与for一样，但是这个是每一次循环是由外部控制开始的，不是自动执行的
     * @param fn(i,next) function 每次循环都执行的函数
     *          i表示当前循环的编号，从0开始，相当于for循环里的i
     *          next是执行下次循环时调用的方法。
     * @param [maxI] int 当i>=maxI时，不会执行回调函数。
     * @return Queue
     * */
    H.doFor = function(fn,maxI){
        return new Queue(fn,maxI);
    };
    function Queue(fn,maxI){
        this.fn = fn;
        //this.totalTime = time;
        this.i = 0;
        this.maxI = maxI;
        this.next();
    }
    /**
     * 如果需要指定下一次执行时的i，则可以设置。
     * 为何不在fn中返回true或者其它标识时自动执行下一次循环？
     * 因为如果fn是异步函数，它返回任何东西的时候都表示它的异步还未完成，所以必须由外部调用这个才能执行下次循环
     * ps.有时候回调函数发现这次循环不需要异步，则直接return true，即可自动进入下一次循环
     * @param i int 这个i相当于for循环里的i，当这次循环的代码出现失败时（如网络请求失败了）可以重新指定下次循环时的i
     * */
    Queue.prototype = {
        next : function(i){
            i = i==undefined?this.i:i;
            if(this.maxI&&i>=this.maxI)return;

            this.i = i+1;
            var res = this.fn.call(this,i,this.next.bind(this)); //用了call，在fn中就不一定用第二个参数了。
            if(res===true)
                this.next();
        }
    };

    //获取字符串字节数(区分中英文) 中文算2个字,英文一个
    H.strLength = function(str){
        var cArr = str.match(/[^\x00-\xff]/ig);
        return str.length + (cArr == null ? 0 : cArr.length);
    };
    //计算字符串的字节数（听说这个效率比上面那个高）
    H.strByte = function(str){
        if(str){
            for(var i= 0,byte=0,len=(str.length||0); i<len; i++){
                byte += (str.charCodeAt(i)>255?2:1);
            }
            return byte;
        }else
            return 0;

    };

    /**
     * 对Date的扩展，将 Date 转化为指定格式的String
     * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符，
     * 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
     * 例子：
     * (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
     * (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
     * @param fmt string
     * @return string
     * */
    Date.prototype.format = function(fmt){ //author: meizz
        var o = {
            "M+": this.getMonth() + 1, //月份
            "d+": this.getDate(), //日
            "h+": this.getHours(), //小时
            "m+": this.getMinutes(), //分
            "s+": this.getSeconds(), //秒
            "q+": Math.floor((this.getMonth() + 3) / 3), //季度
            "S": this.getMilliseconds() //毫秒
        };
        if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o)
            if (new RegExp("(" + k + ")").test(fmt))
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
        return fmt;
    };
    var oldDateParse = Date.parse;
    /**
     * 让Date.parse支持 2017-05-08 15:12:00 这样的格式
     * */
    Date.parse = function (str) {
        var time = Date.parse?oldDateParse(str):false;//ios不兼容某种字符串的格式
        if(!time){
            var date = new Date();
            str = str.split(' ');
            var times = [];
            for (var i=0,sNum=str.length;i<sNum;i++){
                if(i===0){
                    times = str[i].split('-');
                    if(times.length<3)
                        return NaN;
                    date.setFullYear(times[0]);
                    date.setMonth(parseInt(times[1])-1);
                    date.setDate(parseInt(times[2]));
                }else if(i===1){
                    times = str[i].split(':');
                    if(times.length<3)
                        return NaN;
                    date.setHours(parseInt(times[0]));
                    date.setMinutes(parseInt(times[1]));
                    date.setSeconds(parseInt(times[2]));
                    date.setMilliseconds(0);
                }
            }
            time = date.getTime()
        }
        return time;
    };

    /**
     * 根据时间戳来生成时间对象
     * @param time mixed 时间戳
     * @return Date
     * */
    H.time = function(time){
        if(time instanceof Date)
            return time;
        var t = new Date();
        if(time){
            if(typeof time=='number'||(typeof time=='string'&&time.search(/\D/)<0)){
                time = parseInt(time);
                t.setTime(time*(time<10000000000?1000:1));
            }
        }
        return t;
    };
    //获取某一天零时的时间对象
    H.getDayZeroDate = function(date){
        date = this.time(date);
        var newDate = new Date();
        newDate.setFullYear(date.getFullYear());
        newDate.setMonth(date.getMonth());
        newDate.setDate(date.getDate());
        newDate.setHours(0);
        newDate.setMinutes(0);
        newDate.setSeconds(0);
        newDate.setMilliseconds(0);
        return newDate;
    };

    /**
     * 自己写一个适合手机看的，和alert长得像的弹窗，关闭按钮不在右上角的那种
     * @param text string 提示内容
     * @param [btns object] 下面按钮的名字
     * @Param btns.sure string 确定按钮的文字 默认无此按钮
     * @param btns.cancel string 取消按钮的文字 默认无此按钮
     * @param btns.backgroundClose=false boolean 是否允许点击背景关闭弹窗
     * @param btns.closeOther=false 是否打开这个弹窗前关闭其他弹窗
     * @param autoClose int 多久后自动关闭弹窗，这里填的是毫秒
     * */
    H.alert = function(text,btns,autoClose){
        btns = btns||{};
        if(btns.closeOther){
            $('.HAlert').remove();
        }

        var html = '<div class="HAlert" style="position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0,0,0,0.6);z-index: 10000000;color: #282828;">\
                        <div class="alertContent" style="">\
                            <p class="HAlertText" style="margin: 0;line-height: 1.8em;word-wrap: break-word;overflow:auto;Hjs_HAlertTextStyle">hhhjs_text</p>\
                            <div class="HAlertBtns" style="display: none;margin: 5px 5px 0 5px;padding: 5px 0 0 0;border-top: solid 1px #B9B9B9;text-align: center">\
                                <div class="HAlertSure" style="display: none;">hhhjs_sure</div><div class="HAlertCancel" style="display: none;">hhhjs_cancel</div>\
                            </div>\
                        </div>\
                    </div>',
            css = '<style class="HAlertStyle" type="text/css">\
                        .HAlert .HAlertBtns>*{\
                            display: inline-block;\
                            width: 50%;\
                            -webkit-box-sizing: border-box;\
                            -moz-box-sizing: border-box;\
                            box-sizing: border-box;\
                        }\
                        .HAlert .HAlertBtns>*:not(:first-child){border-left: 1px solid #B9B9B9}\
                        .HAlert .alertContent{\
                            position: absolute;\
                            top: 50%;\
                            background: #fff;\
                            border-radius: 8px;\
                            padding: 8px;\
                            left: 50%;\
                            max-width: 80%;\
                            transform: translate(-50%,-50%);\
                            -ms-transform: translate(-50%,-50%);\
                            -moz-transform: translate(-50%,-50%);\
                            -webkit-transform: translate(-50%,-50%);\
                            -o-transform: translate(-50%,-50%);\
                            }\
                        .HAlert.hasBtn .alertContent{min-width: 50%}\
                    </style>',
            $win = $(html
                .replace(/hhhjs_text/g,text||'')
                .replace(/hhhjs_sure/g,btns.sure||'')
                .replace(/hhhjs_cancel/g,btns.cancel||'')
                .replace(/Hjs_HAlertTextStyle/g,'max-height:'+$(window).height()*0.7+'px')),
            $closeBtn = $win.find('.HAlertBtns>*');

        console.error(text);

        $('body').append($win);

        if(btns.sure){
            showBtnContain().find('.HAlertSure').show();
        }else{
            $win.find('.HAlertSure').remove();
        }
        if(btns.cancel){
            showBtnContain().find('.HAlertCancel').show();
        }else{
            $win.find('.HAlertCancel').remove();
        }
        $closeBtn = $win.find('.HAlertBtns>*');
        $closeBtn.css('width',(100/$closeBtn.length)+'%');

        if(btns.backgroundClose){
            $closeBtn = $closeBtn.add($win);
        }

        $closeBtn.click(closeMyself);

        $win.find('.alertContent').click(function(e){
            e.stopPropagation();
        });

        //添加样式
        var $head = $('head');
        if($head.find('.HAlertStyle').length==0){
            $head.append(css);
        }

        //自动关闭
        if(autoClose){
            setTimeout(closeMyself,parseInt(autoClose))
        }

        function closeMyself(){
            $win.remove();
        }

        function showBtnContain(){
            $win.addClass('hasBtn');
            return $win.find('.HAlertBtns').show();
        }

        return $win;
    };

    /**
     * 这是一个对象方法，需要实例化
     * 用于一个页面多次请求同一个接口数据的情况，比如分页。
     * 一个基于jq，且封装好一些功能的用于发起Ajax请求的对象
     * @param url string
     * @param [args] mixed
     * @param fn function
     * @param [option]=null obj
     * @param option.firstPage = 1 int 第一页是几
     * @param option.pageName = page string 接口中“页数”的参数名，默认page
     * @param option.pageNum = null int 每页数据的数量，默认不设置，后台多少数据就多少
     * @param option.pageNumName = pagenum string 接口中“每页数据的数量”的参数名，默认pagenum
     * @param option.autoStart = true boolean 实例化对象之后自动加载第一页数据。
     * @param option.waitForFinish = true boolean 是否必须等待上一次请求完成（不论成功还是失败）才能再用这个对象获取数据。
     * */
    H.Ajax = function(url,args,fn,option){
        if(typeof args=='function'){
            option = fn;
            fn = args;
            args = null;
        }

        this.baseurl = url;
        this.args = args||{};
        this.finishFn = fn;
        this.option = option||{};
        this.pageName = this.option.pageName||'page';
        this.pageNum = this.option.pageNum;
        this.pageNumName = this.option.pageNumName||'pagenum';
        this.waitForFinish = this.option.waitForFinish!==false;
        this.nowPage = typeof this.option.firstPage=='number'?this.option.firstPage-1 : 0;//因为每次获取数据都会先给这个+1，如果传进来参数不-1，则第一次获取的数据就不对了
        this.type = 0;//0可以进行下一次获取数据，1获取数据中，不能进行下一次获取数据。

        if(this.option.autoStart!==false)
            this.next();
    };
    H.Ajax.prototype = {
        /**
         * 获取指定页面的数据
         * @param page int
         * @return boolean 有可能失败的，失败的时候返回false
         * */
        getData:function(page){
            var thi = this;
            if(thi.waitForFinish&&thi.type!=0)
                return false;
            thi.type = 1;
            page = page || thi.nowPage || 1;

            var pageObj = {};
            pageObj[thi.pageName] = page;
            if(thi.pageNum)pageObj[thi.pageNumName] = thi.pageNum;

            var args = typeof thi.args=='object'?
                $.extend(pageObj,thi.args):
                (thi.pageName+'='+page+(thi.pageNum?'&'+thi.pageNumName+'='+thi.pageNum:'')+'&'+thi.args);


            $.ajax(thi.baseurl,{
                data:args,
                dataType:thi.option.dataType||'json',
                success:thi.finishFn,
                complete:function(data){
                    thi.type = 0;
                    thi.option.complete&&thi.option.complete(data);
                }
            });

            return true;
        },
        next:function(){
            this.nowPage++;
            if(!this.getData())
                this.nowPage--;
        }
    };

    /**
     * 找到需要加载原图的元素，并加载
     * 要求这些元素有 needRealImg 这个类，而且 imgSrc 属性写好图片的src
     * 现在的图片会显示默认图，图片加载完成之后才会改成原图
     * ps1. 加载中的图片会有 loadingRealImg class，加载完或者失败的，会删掉这个class和 needRealImg class，
     * 加载失败会添加 loadRealImgErr class，方便css选择器来用
     * ps2.加载中的图片推荐显示这张图 /mobile/themes/images/icons/def.jpg
     * */
    H.showRealImg = function(){
        $('body .needRealImg:not(.loadingRealImg)').each(function(){
            var $this = $(this),
                realImg = $this.attr('imgSrc');
            if(realImg){
                var image = new Image();
                image.onload = function(){
                    setUrlToDom($this,realImg);
                    $this.removeClass('needRealImg loadingRealImg');
                };
                image.onerror = image.onabort = function(){
                    err($this);
                };
                image.src = realImg;
                $this.addClass('loadingRealImg');
            }else{
                err($this);
            }
        });

        function setUrlToDom($this,url){
            if($this[0].nodeName=='IMG')
                $this.attr('src',url);
            else
                $this.css('background-image','url('+url+')');
        }

        function err($this){
            setUrlToDom($this,'themes/images/icons/fail.jpg');
            $this.removeClass('needRealImg loadingRealImg');
            $this.addClass('loadRealImgErr');
        }
    };

    /**
     * 计时程序，可以设置每隔多久回调一次
     * @param fn function 回调时带上从上一次回调到现在回调的时间间隔，和开始时间Date对象，单位毫秒
     * @param inter int 间隔多久回调一次，但外面还是要根据回调函数的参数获取准确的回调间隔，单位毫秒，默认100
     * @param time int 总共要持续多少次，0为不自动停止，默认0
     * */
    H.setInterval = function(fn,inter,time){
        if(!fn)H.error('[H.setInterval]不传函数进来你要干嘛');
        var startTime = new Date(),
            lastTime = startTime,
            durationTime = 0,
            realTime = 0,
            interval = setInterval(function(){
                var nowTime = new Date(),
                    int = nowTime-lastTime;
                lastTime = nowTime;
                durationTime += int;
                realTime++;
                fn(int,startTime,!!(time&&time<=realTime));//第三个参数是告诉回调函数这是最后一次回调。
                if(time&&realTime>=time)
                    clearInterval(interval);
            },inter||100);
        return interval;
    };
    H.clearInterval = function(interval){
        clearInterval(interval);
    };

    /**
     * 获取某个DOM元素下单个字符的宽度和行高，默认是body下的字符。
     * */
    H.getOneChatPX = function(DOM){
        var chatTest = $('<p style="width: 1em;opacity: 0">哈</p>').appendTo($(DOM||'body')),
            data = {
                width:chatTest.width(),
                lineHeight:chatTest.height()
            };
        chatTest.remove();
        return data;
    };

    /**
     * hack在微信等webview中
     * */
    H.setTitle = function(title){
        var $body = $('body');
        document.title = title;
        // hack在微信等webview中无法修改document.title的情况
        var $iframe = $('<iframe src="/favicon.ico" style="display: none"></iframe>').on('load', function() {
            setTimeout(function() {
                $iframe.off('load').remove()
            }, 0)
        }).appendTo($body);
    };

    /**
     * 预加载图片
     * */
    H.preloadImgs = function(imgs,fn){
        for(var i=0,iNum=imgs.length,hasLoaded=0;i<iNum;i++){
            loadImg(imgs[i],function () {
                hasLoaded++;
                if(hasLoaded==iNum){
                    fn();
                }
            })
        }
        function loadImg(src,fn){
            var img = document.createElement('img');
            img.onload = fn;
            img.src = src;
        }
    };

    $.preloadImages = function () {
        for (var i = 0; i < arguments.length; i++) {
            $('<img />').attr('src', arguments[i]);
        }
    };


    //让 getUserMedia 在多浏览器中兼容起来
    navigator.getUserMedia= navigator.getUserMedia|| navigator.webkitGetUserMedia|| navigator.mozGetUserMedia|| navigator.msGetUserMedia;
    window.URL= window.URL|| window.webkitURL|| window.mozURL|| window.msURL;
}());